#include "TCPClient.h"
#include "TCPChannel.h"
#include "Util.h"
#include <iostream>

extern "C"
{
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
}

namespace qy
{

TCPClient::TCPClient(const char *remote, unsigned int port)
: m_channel(NULL), m_remote(remote), m_port(port)
{
}

TCPClient::~TCPClient()
{
	delete_null(m_channel);
}

bool TCPClient::connect()
{
	return connect(m_remote.c_str(), m_port);
}

bool TCPClient::connect(const char *remote, unsigned int port)
{
	int sock = -1;
	struct addrinfo hints;
	struct addrinfo *addrList, *pAddr;

	m_remote = remote;
	m_port = port;

	delete_null(m_channel);

	if(!remote)
   	{
		return false;
   	}

	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
	hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
	hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
	hints.ai_protocol = 0;          /* Any protocol */
	hints.ai_canonname = NULL;
	hints.ai_addr = NULL;
	hints.ai_next = NULL;
	
	if(getaddrinfo(remote, NULL, &hints, &addrList) != 0)
	{
		fatal("Failed to resolve");
	}

	for(pAddr = addrList; pAddr != NULL; pAddr = pAddr->ai_next)
	{
		sock = socket(pAddr->ai_family, pAddr->ai_socktype, pAddr->ai_protocol);
		if(sock < 0) 
		{
			continue;
		}
		struct sockaddr_in *sa = (struct sockaddr_in*)(pAddr->ai_addr);
		sa->sin_port = htons(port);
		char hostIP[64];
		inet_ntop(pAddr->ai_family, &(sa->sin_addr.s_addr), hostIP, 64);
		std::cout << "Connecting " << hostIP << "\n";
		if(::connect(sock, pAddr->ai_addr, pAddr->ai_addrlen) != -1)
		{
			m_channel = new TCPChannel(sock);
			break;
		}
		::close(sock);
	}

	freeaddrinfo(addrList);

	return m_channel != NULL;
}

void TCPClient::close()
{
	delete_null(m_channel);
}

bool TCPClient::canSend(double timeout)
{
	if(m_channel)
	{
		return m_channel->canSend(timeout);
	}
	return false;
}

bool TCPClient::canReceive(double timeout)
{
	if(m_channel)
	{
		return m_channel->canReceive(timeout);
	}
	return false;
}

int TCPClient::send(const char *data, int size)
{
	if(m_channel)
	{
		return m_channel->send(data, size);
	}
	return -1;
}

int TCPClient::receive(char *buffer, int size)
{
	if(m_channel)
	{
		return m_channel->receive(buffer, size);
	}
	return -1;
}

}
